
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <stdio.h>
#include <assert.h>

#include "rp2040.h"

#include "msgpool.h"
#include "umsg.h"

/*
 * 不属于 usb 协议栈的一部分, 仅仅处理消息传递, 
 * 从 usb 传来的消息, 入队列, 通知用户代码, 用户代码再出队列之后使用.
 * 从用户代码发出来, 需要上传 host 的消息, 先入队列, 然后 usb buf 空闲的时候发送给 usb 协议栈.
 * 作为用户代码跟 usb 之间的一个桥梁.
 *
 * usb --> user 初步方案:
 *   user 使用 wfi 唤醒?
 *   队列使用单链表, 无锁.
 */


typedef struct _tag_queue {
    umsg_t * tail;
    umsg_t head;
} uqueue_t;


void umsg_q_init( uqueue_t * pque )
{
    pque->head.next = &(pque->head);
    pque->tail = &(pque->head);
    pque->head.rsvd = 0;
}


void umsg_q_enque( uqueue_t * pque, umsg_t * pnd )
{
    pnd->next = &(pque->head);
    pque->tail->next = pnd;
    pque->tail = pnd;

    pque->head.rsvd += 1;
}


umsg_t * umsg_q_deque( uqueue_t * pque )
{
    umsg_t * pnd;

    if ( pque->head.next == &(pque->head) ) {
        return NULL;
    }

    pnd = pque->head.next;    
    pque->head.next = pnd->next;
    pque->head.rsvd -= 1;

    if ( pque->head.next == &(pque->head) ) {
        pque->tail = &(pque->head);
    }

    return pnd;
}


bool umsg_q_is_empty( uqueue_t * pque )
{
    if ( pque->head.next == &(pque->head) ) {
        return true;
    } else {
        return false;
    }
}


static uqueue_t me2usb;

int umsg_inp_cbk( void * parg, uint8_t * ptr, int tmax )
{
    int mlen;
    umsg_t * pmsg;

    // nothing to send ..
    pmsg = umsg_q_deque( &me2usb );
    if ( pmsg == NULL ) {
        return 0;
    }

    // copy and free
    mlen = pmsg->mlen;
    memcpy( ptr, pmsg->data, mlen );
    msg_free( pmsg );

    //
    return mlen;
}


static uqueue_t usb2me;
extern void  debug_dump_hex( uint8_t * ptr, size_t len );

int umsg_outp_cbk( void * parg, uint8_t * ptr, int tsiz )
{
    umsg_t * pmsg;
    int mlen;

    if ( tsiz <= 0 ) {
        // nothing...
        return 0;
    }

    // printf( "outp, %d\n", tsiz );
    // debug_dump_hex( ptr, tsiz );

    pmsg = (umsg_t *)msg_alloc();
    if ( pmsg == NULL ) {
        printf("alloc null, outp\n");
        return 0;
    }

    // copy from usb dpram.
    mlen = tsiz - 1;
    pmsg->mlen = (uint8_t)mlen;
    pmsg->type = ptr[mlen];
    memcpy( pmsg->data, ptr, tsiz );

    // add to tail..
    umsg_q_enque( &usb2me, pmsg );
    return 0;
}


extern int ttusb_intf_send( void * pifc, int idx, int tlen, void * pdat );

void umsg_send_to_host( void * pifc, int idx, umsg_t * pmsg )
{
    int iret;
    int mlen;

    assert( pmsg->mlen <= 63 );
    mlen = pmsg->mlen;
    pmsg->data[mlen] = pmsg->type;
    mlen += 1;
    pmsg->mlen = (uint8_t)mlen;

    /* me2usb queue not empty ??? */
    if ( true != umsg_q_is_empty( &me2usb ) ) {
        umsg_q_enque( &me2usb, pmsg );
        return;
    }

    /* send to host? */
    iret = ttusb_intf_send( NULL, idx, mlen, pmsg->data );
    if ( iret == 0 ) {
        msg_free( pmsg );
        return;
    }

    /* add to queue */
    umsg_q_enque( &me2usb, pmsg );
    return;
}


#include "hardware/structs/sio.h"

void FIFO_P0_Handler( void )
{
    umsg_t * pmsg;

    while ( 0 != (sio_hw->fifo_st & SIO_FIFO_ST_VLD_BITS) ) {

        pmsg = (umsg_t *)(uintptr_t) sio_hw->fifo_rd;
        
        //printf("fifo irq, %p\n", pmsg );

        umsg_send_to_host( NULL, 0, pmsg );
    }
    
    // clear other error flag.
    sio_hw->fifo_st = 0xff;
}


/*
 * 在 core 0 上运行, 当前处理 umsg 的 user 层面交互.
 * 好像作用就是自动关闭中断? 代码隔离?...
 */
uintptr_t SVC_usb_handler( int sid, uintptr_t arg0, uintptr_t regs[] )
{
    uintptr_t uret = 0;

    switch ( sid )  {

    case 2:
        uret = (uintptr_t)umsg_q_deque( &usb2me );
        break;

    case 3:
        umsg_send_to_host( NULL, 0, (umsg_t *)arg0 );
        break;

    default:
        printf("scall, %d\n", sid );
        break;
    }

    return uret;
}



void umsg_init( void )
{
    umsg_q_init( &me2usb );
    umsg_q_init( &usb2me );
}







